/*
 * VisFactory.c
 *
 *  Created on: 19.07.2011
 *      Author: sili
 */

#include "VisFactory.h"
#include "AALItem.h"

#include "Visualizer.h"
#include "PropertyFactory.h"
#include "SystemFactory.h"

static log4cxx::LoggerPtr logger(log4cxx::Logger::getLogger("at.sili.aalsystem.factories.VisFactory"));

static pthread_rwlock_t visLock = PTHREAD_RWLOCK_INITIALIZER;

AALAction * lastAction = NULL;

VisFactory::VisFactory(Controller * _controller)
	: ActionListener("VisFactory"){
	this->controller = _controller;
	this->controller->addListener(this);

	this->visualizer = NULL;

	this->shutdown = false;

	this->showView.clear();
	this->infoView.clear();
	this->warnView.clear();
	this->alarmView.clear();

	AAL_DEBUG(logger, "Constructor");
}

VisFactory::~VisFactory(){
	AAL_DEBUG(logger, "Destructor");
}

Controller * VisFactory::getController(){
	return this->controller;
}


void VisFactory::destroyVisualizer(){
	this->visualizer = NULL;
}

void VisFactory::saveAction(AALAction * _action){

	pthread_rwlock_rdlock(&visLock);
	int timeToLive = 0;
	if(_action->type == doInfo)
			timeToLive = 4;
	if(_action->type == doWarn)
			timeToLive = 7;

	AALAction * newAction = new AALAction(_action->type, _action->aalItem, _action->message.c_str(), timeToLive, this->controller);

	if(newAction->type == doShow){
		this->showView.push_back(newAction);
	} else if(newAction->type == doInfo) {

		// Delete all existing doInfo - Actions.
		// Only one doInfo - Action will be saved and shown.
		vector<AALAction *>::iterator entry;
		for (entry=this->infoView.begin(); entry<this->infoView.end(); entry++ ){
			delete(*entry);
		}
		this->infoView.clear();

		this->infoView.push_back(newAction);
	} else if(newAction->type == doWarn) {
		this->warnView.push_back(newAction);
	} else if(newAction->type == doAlarm) {
		this->alarmView.push_back(newAction);
	}
	pthread_rwlock_unlock(&visLock);
}

int VisFactory::getNumOfActions(){

	pthread_rwlock_rdlock(&visLock);

	int nrOfVisActions = 0;
	nrOfVisActions += this->showView.size();
	nrOfVisActions += this->infoView.size();
	nrOfVisActions += this->warnView.size();
	nrOfVisActions += this->alarmView.size();

	pthread_rwlock_unlock(&visLock);
	return nrOfVisActions;
}

void VisFactory::removeAction(vector<AALAction *> * _workingBase, AALAction * _action){
	AAL_DEBUG(logger, "Queue size before removeAction: %d.", _workingBase->size());

	vector<AALAction *>::iterator entry;
	for (entry=_workingBase->begin(); entry<_workingBase->end(); entry++ ){
		if(_action==(*entry)){
			AAL_TRACE(logger, "Deleting Action id:%ld actionType: %s aalItemId: %d", _action->id, _action->typeToString(), _action->aalItem != NULL ? _action->aalItem->getId():-1);
			_workingBase->erase(entry);
			delete(_action);
			break;
		}
	}
	AAL_DEBUG(logger, "Queue size after removeAction: %d.", _workingBase->size());
}

int VisFactory::discardQueuedAction(){

	AALAction * currentAction = getNextAction();

	pthread_rwlock_rdlock(&visLock);
	if(currentAction){
		if(currentAction->type == doShow){

			int initDepth = currentAction->aalItem->depth;
			// Special case for the goBack action.
			// e.g. If currently a room with 5 sensors is shown and the user enumerates each of the
			//      shown sensors, then we will generate 5 doShow - Actions and user needs to push
			//      the back buton 5 times to get back to the parent of the room (the flat).
			//      So to avoid this, go delete every doShow - Action until we get one which isn't from the same level.
			removeAction(&this->showView, currentAction);

			currentAction = getNextAction();
			while (currentAction && currentAction->type == doShow && currentAction->aalItem->depth == initDepth){
				removeAction(&this->showView, currentAction);
				currentAction = getNextAction();
			}
		} else if(currentAction->type == doInfo) {
			removeAction(&this->infoView, currentAction);
		} else if(currentAction->type == doWarn) {
			removeAction(&this->warnView, currentAction);
		} else if(currentAction->type == doAlarm) {
			removeAction(&this->alarmView, currentAction);
		}
	}

	pthread_rwlock_unlock(&visLock);

	int result = getNumOfActions();
	AAL_DEBUG(logger, "There are still %d of visActions in the queue...", result);
	return result;
}

AALAction * VisFactory::getNextAction(){

	pthread_rwlock_rdlock(&visLock);

	AALAction * nextAction = NULL;

	if(this->alarmView.size() > 0){
		// FIRST priority: alarms
		nextAction = alarmView.front();
	} else if(this->warnView.size() > 0){
		// SECOND priority: warnings
		nextAction = this->warnView.back();
	} else if(this->infoView.size() > 0){
		// THIRD priority: infos
		nextAction = this->infoView.back();
	} else if(this->showView.size() > 0){
		// FORUTH priority: shows
		nextAction = this->showView.back();
	}
	pthread_rwlock_unlock(&visLock);
	return nextAction;
}

cOsdObject * VisFactory::mainMenuAction(){
	if(this->visualizer ==  NULL){
		AAL_INFO(logger, "MainMenuAction in VisFactory starting new visualizer...");
		this->visualizer = new Visualizer(this);
	}

	if(this->getNextAction() == NULL){
		int defaultFlatId = this->controller->getPropertyFactory()->getIntProperty(AALPROP_DEFAULTFLAT,0);
		AAL_DEBUG(logger, "MainMenuAction using defaultFalt id:%d", defaultFlatId);
		this->controller->publish(doShow,"",this->controller->getRootAALItem()->getAALItem(defaultFlatId));
	}

	return this->visualizer;
}

void VisFactory::shutdownOsd(){
	pthread_rwlock_rdlock(&visLock);
	if(this->visualizer != NULL)
		this->visualizer->Exit();
	while(this->getNumOfActions() != 0){
		discardQueuedAction();
	}
	/*
	this->showView.clear();
	this->warnView.clear();
	this->alarmView.clear();
	*/
	pthread_rwlock_unlock(&visLock);
}

void VisFactory::doAction(AALAction * _action){
	pthread_rwlock_rdlock(&visLock);
	if( _action->type == doInfo || _action->type == doShow ||
		_action->type == doWarn || _action->type == doAlarm){

		AAL_INFO(logger, "%s doAction id:%ld type: %s message:\"%s\" aalItemId:%d", ((ActionListener*)this)->getName(),_action->id, _action->typeToString(), _action->message.c_str(), _action->aalItem != NULL ? _action->aalItem->getId():-1);
		this->saveAction(_action);

		/*
		if(this->visualizer == NULL){
			cRemote::CallPlugin("vdr4aal");
		} else {
			this->visualizer->Show();
		}
		*/

	} else if(_action->type == doHide){
		shutdownOsd();
	} else if(_action->type == doReaload && this->visualizer){

		DrawPane * cp = this->visualizer->getContentPane();
		if(cp){
			vector<AALItem*>::iterator entry = cp->currentVisibleItems.begin();
			while(entry != cp->currentVisibleItems.end()){
				AALItem * visibleItem = (*entry);
				if(visibleItem == _action->aalItem){
					AALAction * currentAction = getNextAction();
					currentAction->doReload = true;
					AAL_DEBUG(logger, "RELOAD aalItem.id:%d", _action->aalItem != NULL ? _action->aalItem->getId():-1);
					break;
				}
				entry++;
			}
		}
	}
	pthread_rwlock_unlock(&visLock);
}

void VisFactory::doMainSavedAction(){
	pthread_rwlock_rdlock(&visLock);
	AALAction * currentAction = getNextAction();

	if(currentAction != lastAction){
		lastAction = currentAction;

		if(currentAction != NULL){
			currentAction->resetTimeToLiveTimer();
			if(this->visualizer == NULL){
				cRemote::CallPlugin("vdr4aal");
			} else {
				this->visualizer->Show();
			}
		}
	} else{
		// action is the same as previous
		// check if the action is still alive
		if(currentAction && !currentAction->isAlive()){
			AAL_DEBUG(logger, "SHUTDOWN THIS ACTION %ld.", ((ActionListener*)this)->getName(), currentAction->id);
			discardQueuedAction();
		} else {
			if(this->visualizer && currentAction->doReload){
				this->visualizer->Show();
				currentAction->doReload = false;
			}
		}
	}
	pthread_rwlock_unlock(&visLock);
}
